﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XDL.Framework.Screens;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using XDL.Framework.Particles;
using XDL.Framework.Input;
using XDL.Framework;

namespace JumpNFight.Screens
{
    class GameplayScreen : GameScreen
    {
        Timer m_musicTimer = new Timer(new TimeSpan(0, 0, 0, 0, 250));
        // game content
        AudioManager m_audio;
        ContentManager m_content;
        RenderLayer m_worldRenderer;
        Simulation.World m_world;
        bool m_gameOver = false;
        bool m_isPaused = false;
        // particles
        ParticleSystemCollection m_particleManager;
        Matrix m_projectionTransform;
        Matrix m_viewTransform = Matrix.Identity;

        public GameplayScreen()
        {
            TransitionOnTime = TimeSpan.FromSeconds(0.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);
        }

        public void Initialize()
        {
            Viewport viewport = GameServices.Viewport;
            Matrix projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);
            Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);

            m_projectionTransform = halfPixelOffset * projection;

            // retrieves the particle manager
            m_particleManager = ScreenManager.GetService<ParticleSystemCollection>();
            m_particleManager.SetCamera(m_viewTransform, m_projectionTransform);

            m_audio = GameServices.GetService<AudioManager>();
            m_world = GameServices.GetService<Simulation.World>();
            m_world.Start();

            m_worldRenderer = new RenderLayer(ScreenManager.Game);
            m_worldRenderer.Initialize();
        }

        public override void LoadContent()
        {
            m_content = new ContentManager(ScreenManager.Game.Services, "Content");

            base.LoadContent();
        }

        public override void UnloadContent()
        {
            m_content.Unload();
            base.UnloadContent();
        }
        
        public override void Update(Microsoft.Xna.Framework.GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
            // if this screen is leaving, then stop the music & some other cleanup
            if (IsExiting)
            {
                // 
                m_world.Stop();
                m_worldRenderer.Stop();

                // 
                m_audio.StopMusic();
                //
                m_particleManager.Clear();
            }
            else if ((otherScreenHasFocus == true) || (coveredByOtherScreen == true))
            {
                // make sure nobody's controller is vibrating
                for (int i = 0; i < Simulation.World.MaxPlayers; i++)
                {
                    GamePad.SetVibration((PlayerIndex)i, 0f, 0f);
                }
                if (!m_isPaused)
                {
                    // special effects
                    m_particleManager.Update(gameTime);

                    // Update world
                    m_world.Update(gameTime);

                    // Update rendering status
                    m_worldRenderer.Update(gameTime);
                }
            }
            else
            {
                m_musicTimer.Update(gameTime);
                if (m_musicTimer.IsSignaled)
                {
                    m_audio.PlayMusic("BackgroundMusic");                    
                }

                // check for a winner
                if (m_gameOver == false)
                {
                    if (m_world.IsGameOver)
                    {
                        m_gameOver = true;

                        GameOverPopup gameOverScreen = new GameOverPopup();
                        gameOverScreen.OnExit += OnExit;
                        ScreenManager.AddScreen(gameOverScreen);
                    }
                }
                // special effects
                m_particleManager.Update(gameTime);

                // Update world
                m_world.Update(gameTime);

                // Update rendering status
                m_worldRenderer.Update(gameTime);
            }
        }

        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(GameTime gameTime, InputState input)
        {
            if (input.PauseGame)
            {
                m_isPaused = true;
                // If they pressed pause, bring up the pause menu screen.
                PausePopup pauseScreen = new PausePopup();
                pauseScreen.OnClose += (o, e) => { m_isPaused = false; };
                pauseScreen.OnExitToMainMenu += (o, e) => 
                {
                    LoadingScreen.Load(ScreenManager, QuitToMainMenu, false);         
                };
                ScreenManager.AddScreen(pauseScreen);
                return;
            }

            if (!m_gameOver)
            {
                for (int i = 0; i < Simulation.World.MaxPlayers; i++)
                {
                    HandleInput(gameTime, input, (PlayerIndex)i);
                }
            }
        }

        void QuitToMainMenu(object sender, EventArgs e)
        {
            LoadingScreen.Load(ScreenManager, LoadMainMenuScreen, false);
        }

        /// <summary>
        /// Loading screen callback for activating the main menu screen,
        /// used when quitting from the game.
        /// </summary>
        void LoadMainMenuScreen(object sender, EventArgs e)
        {
            ScreenManager.AddScreen(new BackgroundScreen());
            // create a new instance
            // push instance into the active screen stack
            ScreenManager.AddScreen(new MainMenuScreen());
        }

        void HandleInput(GameTime gameTime, InputState input, PlayerIndex index)
        {
            if (input.StartGame(index))
                m_world.AddPlayer(index);

            if (m_world.AcceptInput(index))
            {
                Vector2 move = input.Move(index);

                m_world.Move(index, new Vector2(move.X, 0));
                if ( input.IsActionPressed(index) || move.Y == 1)
                    m_world.Jump(index);

                if ( input.IsFirePressed(index, m_world.IsSingleShot(index)) )
                    m_world.Fire(gameTime, index);
            }
        }

        /// <summary>
        /// Handles (asynchronous) exit event from the Game Over (sucess of failure) dialog box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnExit(object sender, DialogBoxScreen.DialogBoxResultEventArgs e)
        {
            if (e.Result)
            {
                // start over immediately
                m_world.Stop();
                m_worldRenderer.Stop();

                // start again
                m_world.Start();
                m_gameOver = false;
            }
            else
            {
                // 
                LoadingScreen.Load(ScreenManager, QuitToMainMenu, false);         
            }
        }

        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            // helper
            GraphicsDevice gd = ScreenManager.Game.GraphicsDevice;

            // draw the world!
            m_worldRenderer.Draw(gameTime);

            // particles
            m_particleManager.SetCamera(m_viewTransform, m_projectionTransform);
            m_particleManager.Draw(gameTime);

            // If the game is transitioning on or off, fade it out to black.
            if (TransitionPosition > 0)
                ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
        } 
    }
}
